<?php

declare(strict_types=1);

namespace App\CustHub\Service;

use App\Common\Robot\Gateways\Gateway;
use App\CustHub\Constants\RoomOpTypeConstants;
use App\CustHub\Constants\RoomTaskCenterTypeConstants;
use App\CustHub\Event\RoomOperationBehaviorAfter;
use App\CustHub\Event\SmartTagRoomBehaviorTrigger;
use App\CustHub\Event\UserLeftRoomEvent;
use App\CustHub\Helpers\TagReplacer;
use App\CustHub\Mapper\ChContactWhiteMapper;
use App\CustHub\Mapper\ChRobotMapper;
use App\CustHub\Mapper\ChRoomMapper;
use App\CustHub\Mapper\ChRoomMemberMapper;
use App\CustHub\Model\ChRobot;
use App\CustHub\Model\ChRoom;
use App\CustHub\Model\ChRoomMember;
use App\CustHub\Model\ChRoomRobot;
use App\Robot\Service\SendRobotMessageService;
use Carbon\Carbon;
use FriendsOfHyperf\Lock\Exception\LockTimeoutException;
use Hyperf\Di\Annotation\Inject;
use Mine\Abstracts\AbstractService;
use Mine\Annotation\Transaction;
use function FriendsOfHyperf\Lock\lock;
use function Hyperf\Collection\collect;

class RoomReceiveCallBackService extends AbstractService
{
    public $mapper;

    #[Inject]
    protected ChRobotMapper $robotMapper;

    #[Inject]
    protected ChContactWhiteMapper $contactWhiteMapper;

    #[Inject]
    protected ChContactBlackService $blackService;

    #[Inject]
    protected ChConfigService $configService;

    #[Inject]
    protected ChRoomMemberMapper $roomMemberMapper;

    #[Inject]
    protected SendRobotMessageService $sendRobotMessageService;


    public function __construct(ChRoomMapper $mapper)
    {
        $this->mapper = $mapper;
    }

    public function checkRoom(ChRobot $robot,string $roomId): void
    {
        stdout_log()->info('检测群信息是否存在'.$robot->id.'---'.$roomId);
        $lock = lock('auto_room_check:'.md5($robot->main_uid.'_'.$roomId), 30);
        try {
            $lock->block(20);
            $info = $this->mapper->findByRobotIdAndRoomWxid($roomId,$robot->id);
            if(empty($info)){
                stdout_log()->info('检测群信息是不存在'.$robot->id.'---'.$roomId);
                container()->get(ChRoomSyncService::class)->syncRoomBySession($robot,$roomId);
            }
        } catch (LockTimeoutException $e) {
        } finally {
            $lock->release();
        }
    }

    public function roomMessage(ChRobot $robot, array $data): void
    {
        $content = $data['content'];
        $sender = (string)$data['sender'];
        if(str_starts_with($content, '踢@')){
            $roomId = (string)$data['from_room_id'];
            //判断是否是白名单用户以及判断当前机器人是否是群主或者管理员
            if($this->contactWhiteMapper->checkContactExists($sender,$robot->main_uid) && $this->roomMemberMapper->isRoomOwnerOrAdmin($roomId,$sender)){
                //踢出群聊
                $atList = $data['at_list'];
                //需要踢出群聊的客户
                $userList = [];
                foreach ($atList as $at){
                    $exists = $this->contactWhiteMapper->checkContactExists($at['user_id'],$robot->main_uid);
                    if($exists){
                        continue;
                    }
                    $userList[] = $at['user_id'];
                }

                if(!empty($userList)){
                    //发送踢人话术
                    $config = $this->configService->getConfigByGroupKey($robot->uid,$robot->main_uid,'white_config');
                    $kickMessage = $config['kick_message']['value'];
                    if(!empty($kickMessage)){
                        $violatingMemberNickname = $this->roomMemberMapper->model::query()
                            ->where('room_wxid',$roomId)
                            ->whereIn('user_id',$userList)
                            ->pluck('nickname')
                            ->implode('、');

                        $whiteListNickname = $this->roomMemberMapper->model::query()
                            ->where('room_wxid',$roomId)
                            ->where('user_id',$sender)
                            ->value('nickname');

                        $message = TagReplacer::replaceTags($kickMessage,[
                            'violating_member_nickname' => $violatingMemberNickname,
                            'white_list_nickname' => $whiteListNickname
                        ]);

                        $this->sendRobotMessageService->handle([
                            'robot_id' => $robot->id,
                            'msg_type' => 'text',
                            'content' => $message,
                            'to_id' => $roomId
                        ]);
                    }

                    $robotApiService = make(\App\Common\Robot\Robot::class, [$robot->server_id, $robot->uuid])->gateway();
                    $rst = $robotApiService->removeRoomMember($roomId,$userList);
                    if($rst !== false){
                        //变更用户在群状态
                        $this->roomMemberMapper->outRoom($roomId,$userList);
                        //将成员加入黑名单
                        $this->blackService->saveBlack([
                            'uid' => $robot->uid,
                            'main_uid' => $robot->main_uid,
                            'user_ids' => $userList,
                            'add_type' => 0
                        ],false);
                    }
                }
            }
        }
    }


    /**
     * 群操作相关
     * @param array $data
     * @return void
     */
    public function roomOp(array $data,ChRobot $robotInfo): void
    {
        $roomId = (string)$data['from_room_id'];
        $userId = (string)$data['user_id'];
        $roomOpType = $data['room_op_type'];

        $roomInfo = $this->mapper->findByRoomWxid($roomId,$robotInfo->main_uid);
        $noCheckRoomType = [
            RoomOpTypeConstants::ROOM_INFO_CHANGE,
            RoomOpTypeConstants::JOIN_ROOM,
            RoomOpTypeConstants::CREATE_ROOM,
            RoomOpTypeConstants::OUT_ROOM,
            RoomOpTypeConstants::OWNER_OR_ADMIN_REMOVE,
        ];
        if(empty($roomInfo) && !in_array($roomOpType,$noCheckRoomType)){
            return;
        }
        switch ($roomOpType) {
            case RoomOpTypeConstants::ZHUAN_RANG_QUN_ZHU:
                //转让群主
                $this->changeRoomOwner($roomInfo, $userId, $robotInfo);
                break;
            case RoomOpTypeConstants::ROOM_INFO_CHANGE:
                $this->editRoomInfo($roomId, $robotInfo);
                break;
            case RoomOpTypeConstants::EDIT_ROOM_NAME:
                //修改群名称
                $this->editRoomName($roomInfo, $data['room_name']);
                break;
            case RoomOpTypeConstants::JOIN_ROOM:
                //用户入群
                $this->joinRoom($robotInfo, $data);
                break;
            case RoomOpTypeConstants::CREATE_ROOM:
                //创建群
                $this->editRoomInfo($roomId, $robotInfo);
                break;
            case RoomOpTypeConstants::OUT_ROOM:
            case RoomOpTypeConstants::OWNER_OR_ADMIN_REMOVE:
                $this->outRoom($roomId, $data['msg_data'],$robotInfo->id,$robotInfo->main_uid);
                event(new UserLeftRoomEvent($roomId));
                break;
            case RoomOpTypeConstants::ROOM_NAME_CHANGE_RESTRICTED:
                $this->mapper->update($roomInfo->id,[
                    'room_is_forbid_change_name' => 1
                ]);
                break;
            case RoomOpTypeConstants::IS_ROOM_NAME_CHANGE_ALLOWED:
                $this->mapper->update($roomInfo->id,[
                    'room_is_forbid_change_name' => 0
                ]);
                break;
            case RoomOpTypeConstants::IS_ROOM_INVITE_CONFIRMATION_ENABLED:
                $this->mapper->update($roomInfo->id,[
                    'room_is_invite_confirmation' => 1
                ]);
                break;
            case RoomOpTypeConstants::IS_ROOM_INVITE_CONFIRMATION_DISABLED:
                $this->mapper->update($roomInfo->id,[
                    'room_is_invite_confirmation' => 0
                ]);
                break;
            case RoomOpTypeConstants::EDIT_ROOM_NOTICE:
                $this->mapper->update($roomInfo->id,[
                    'room_announcement' => $data['content']
                ]);
                break;
            case RoomOpTypeConstants::DISMISS_ROOM:
                //解散群
                $this->dismissRoom($roomId);
                break;
        }
        if(!empty($roomInfo)){
            $this->clearRoomCache($roomInfo->id,$roomInfo->main_uid, $roomInfo->room_wxid, $robotInfo->id);
        }
    }

    private function clearRoomCache(int $id,int $mainUid,string $roomWxid,int $robotId): void
    {
        $this->mapper->clearCacheById($id);
        $this->mapper->clearCacheByRoomWxId($roomWxid,$mainUid);
        $this->mapper->clearCacheByRobotIdAndRoomWxid($roomWxid,$robotId);
    }

    /**
     * 变更群主
     * @param string $roomId
     * @param string $newOwnerId
     * @return void
     */
    #[Transaction]
    private function changeRoomOwner(ChRoom $roomInfo, string $newOwnerId, ChRobot $robotInfo): void
    {
        $roomId = $roomInfo->room_wxid;
        if (!empty($roomInfo) && $roomInfo['create_user_id'] != $newOwnerId) {
            $service = make(\App\Common\Robot\Robot::class, [$robotInfo->server_id, $robotInfo->uuid])->gateway();
            $result = $service->getContactDetail([$newOwnerId]);
            $nickname = '';
            if ($result !==false && !empty($result['contact_list'])) {
                $detail = array_shift($result['contact_list']);
                $nickname = $detail['nickname'];
            }

            $this->mapper->update($roomInfo->id, [
                'create_user_id' => $newOwnerId, //群主ID
                'room_owner_name' => $nickname,
                'room_owner_time' => Carbon::now()->toDateTimeString(),
            ]);

            if (!$roomInfo->robots()->wherePivot('robot_id', $robotInfo->id)->exists()) {
                $roomInfo->robots()->attach($robotInfo->id);
            }


            ChRoomMember::query()->where('room_wxid', $roomId)->where('is_owner', 1)->update([
                'is_owner' => 0
            ]);

            ChRoomMember::query()->where('room_wxid', $roomId)->where('user_id', $newOwnerId)->update([
                'is_owner' => 1,
                'is_admin' => 0,
            ]);

            event(new RoomOperationBehaviorAfter($roomInfo->id, RoomTaskCenterTypeConstants::TRANSFER_ROOM_OWNER, [
                'nickname' => $nickname,
            ]));
        }
    }

    public function syncRoomHandler(array $data,ChRobot $robotInfo): void
    {
        $roomInfo = $this->mapper->findByRoomWxid($data['from_room_id'],$robotInfo->main_uid);
        if(!empty($roomInfo)){
            $this->editRoomInfo($data['from_room_id'], $robotInfo);
        }
    }

    /**
     * 群信息变更
     * @param string $roomId
     * @param ChRobot $robotInfo
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    private function editRoomInfo(string $roomId, ChRobot $robotInfo): void
    {
        $robotApiService = make(\App\Common\Robot\Robot::class, [$robotInfo->server_id, $robotInfo->uuid])->gateway();

        $roomDetailList = $robotApiService->getRoomDetail([$roomId]);
        if($roomDetailList !== false){
            $roomDetail = array_shift($roomDetailList);
            //判断是否有退群，在保存群数据之前查询一次群人数
            $roomDatabaseInfo = $this->mapper->findByRoomWxid($roomDetail['room_id'],$robotInfo->main_uid);
            if($roomDatabaseInfo){
                if(!empty($roomDatabaseInfo) && count($roomDetail['member_list'] ?? []) < $roomDatabaseInfo['room_member_count']){
                    container()->get(ChRoomCodeService::class)->saveRelationStatus(null,$roomDatabaseInfo->id,$roomDatabaseInfo->main_uid,$roomDatabaseInfo->uid,$roomId,2);
                }
                //群名称变更
                if($roomDatabaseInfo->room_name != $roomDetail['room_name']){
                    container()->get(ChRoomAutoTagConfigService::class)->getMeetTheConditionScheme($roomDatabaseInfo->main_uid,$roomDatabaseInfo->room_wxid,$roomDetail['room_name'],count($roomDetail['member_list'] ?? []));
                }
            }

            container()->get(ChRoomSyncService::class)->saveRoom($robotApiService, $roomDetail, $robotInfo->uid, $robotInfo->main_uid, $robotInfo->id);
            $roomDatabaseInfo = $this->mapper->findByRoomWxid($roomDetail['room_id'],$robotInfo->main_uid);
            //更新群活码roomId
            if(!empty($roomDatabaseInfo)){
                $codeService = container()->get(ChRoomCodeService::class);
                $codeId=$codeService->syncRelationRoomId($roomDatabaseInfo->main_uid,$roomDatabaseInfo->room_wxid,$roomDatabaseInfo->id);
                $codeId && $codeService->addTags($roomDatabaseInfo->id,$codeId);
            }
        }
    }

    /**
     * 修改群名称
     * @param string $roomId
     * @param string $roomName
     * @return void
     */
    private function editRoomName(ChRoom $roomInfo, string $roomName): void
    {
        $this->mapper->update($roomInfo->id,['room_name' => $roomName]);

        //群自动打标签
        container()->get(ChRoomAutoTagConfigService::class)->getMeetTheConditionScheme($roomInfo->main_uid,$roomInfo->room_wxid,$roomInfo->room_name,$roomInfo->room_member_count);

        event(new RoomOperationBehaviorAfter($roomInfo->id, RoomTaskCenterTypeConstants::CHANGE_ROOM_NAME, [
            'roomName' => $roomName,
        ]));
    }


    /**
     * 加入群聊
     * @param int $robotId
     * @param array $data
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \RedisException
     */
    private function joinRoom(ChRobot $robotInfo, array $data): void
    {
        $robotId = $robotInfo->id;
        $roomWxid = (string)$data['from_room_id'];
        $changedMemberList = explode(';', $data['msg_data']['changed_member_list']);
        foreach ($changedMemberList as $k=>$item){
            $changedMemberList[$k] = decodeIfBase64($item);
        }

        $roomMemberList = [];
        foreach ($changedMemberList as $item){
            $item = explode(';', $item);
            foreach ($item as $v){
                $roomMemberList[] = $v;
            }
        }
        $changedMemberList = $roomMemberList;
        //当前进群人数
        $memberCount = count($changedMemberList);

        $roomInfo = $this->mapper->findByRoomWxid($roomWxid,$robotInfo->main_uid);
        if (empty($roomInfo)) {
            $uid = $robotInfo->uid;
            $mainUid = $robotInfo->main_uid;
            $robotApiService = make(\App\Common\Robot\Robot::class,[$robotInfo->server_id,$robotInfo->uuid])->gateway();
            //同步群聊到本地
            $this->syncToDatabase($robotApiService, $roomWxid, $uid, $mainUid, $robotId);
            $roomInfo = $this->mapper->findByRoomWxid($roomWxid,$robotInfo->main_uid);
            $roomMemberCount = $roomInfo->room_member_count;
        } else {

            //查询用户是否在群内
            $exitsMemberList = $this->roomMemberMapper->model::query()
                ->where('room_wxid',$roomWxid)
                ->whereIn('user_id',$changedMemberList)
                ->where('is_out',0)
                ->get()
                ->pluck('user_id')->toArray();
            $changedMemberList = array_diff($changedMemberList,$exitsMemberList);
            if(empty($changedMemberList)){
                return;
            }
            //当前进群人数
            $memberCount = count($changedMemberList);

            $memberList = [];
            foreach ($changedMemberList as $item) {
                $memberList[] = [
                    'user_id' => $item,
                    'inviter_id' => $data['sender'],
                    'join_time' => time(),
                    'is_out' => 0,
                    'out_time' => null
                ];
            }

            //同步群成员到本地
            $this->addRoomMember($roomInfo->create_user_id, $robotInfo, $roomWxid, $memberList);
            $roomMemberCount = $roomInfo->room_member_count + $memberCount;
            //更新群成员数量
            $roomInfo->room_member_count = $roomMemberCount;
            $roomInfo->save();
        }

        $roomMemberName = $this->roomMemberMapper->model::query()
            ->where('room_wxid',$roomWxid)
            ->whereIn('user_id',$changedMemberList)
            ->pluck('nickname')
            ->implode('、');

        //入群欢迎语
        container()->get(ChRoomWelcomeService::class)->getMeetTheConditionScheme($robotId,$robotInfo->main_uid, $roomWxid, $memberCount,$roomMemberName,$changedMemberList);
        //群邀请任务
        container()->get(ChRoomInviteSendTaskService::class)->check($roomWxid,$changedMemberList);

        //群自动打标签
        container()->get(ChRoomAutoTagConfigService::class)->getMeetTheConditionScheme($roomInfo->main_uid,$roomWxid,$roomInfo->room_name,$roomMemberCount);

        //群活码记录登记
        container()->get(ChRoomCodeService::class)->saveRelationStatus($changedMemberList,$roomInfo->id,$roomInfo->main_uid,$roomInfo->uid,$roomWxid,1);

        foreach ($changedMemberList as $item){
            //社群行为智能标签
            event(new SmartTagRoomBehaviorTrigger($robotInfo->id, $item, $roomWxid));
        }
    }

    /**
     * 新增群成员
     * @param string $createUserId
     * @param ChRobot $robotInfo
     * @param string $roomWxid
     * @param array $memberList
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    private function addRoomMember(string $createUserId, ChRobot $robotInfo, string $roomWxid, array $memberList): void
    {
        ;
        $uid = $robotInfo->uid;
        $mainUid = $robotInfo->main_uid;

        $robotApiService = make(\App\Common\Robot\Robot::class,[$robotInfo->server_id,$robotInfo->uuid])->gateway();
        $robotId = $robotInfo->id;
        container()->get(ChRoomSyncService::class)->addRoomMember($robotApiService, $uid, $mainUid,$robotId, $roomWxid, $createUserId, $memberList);
    }

    /**
     * 存储到本地
     * @param Gateway $robotApiService
     * @param string $roomWxid
     * @param int $uid
     * @param int $mainUid
     * @param int $robotId
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    private function syncToDatabase(Gateway $robotApiService, string $roomWxid, int $uid, int $mainUid, int $robotId)
    {
        $roomDetailList = $robotApiService->getRoomDetail([$roomWxid]);
        $roomDetail = array_shift($roomDetailList);

        container()->get(ChRoomSyncService::class)->saveRoom($robotApiService, $roomDetail, $uid, $mainUid, $robotId);
        if (!empty($roomName)) {
            $robotApiService->editRoomName($roomWxid, $roomName);
        }
    }

    /**
     * 退群
     * @param int $roomId
     * @param array $data
     * @return void
     */
    private function outRoom(string $roomWxid, array $data,int $robotId,int $mainUid): void
    {
        foreach ($data as $item) {
            $memberIds = explode(';', $item);

            foreach ($memberIds as $k=>$memberId){
                $memberIds[$k] = decodeIfBase64($memberId);
            }

            ChRoomMember::query()
                ->where('room_wxid', $roomWxid)
                ->whereIn('user_id', $memberIds)
                ->update([
                    'is_admin' => 0,
                    'is_out' => 1,
                    'out_time' => Carbon::now()->toDateTimeString()
                ]);

            //减少群成员数量
            $this->mapper->decrementRoomMemberCount($mainUid,$roomWxid,count($memberIds));

            //跟新退群数量
            $roomInfo = $this->mapper->findByRoomWxid($roomWxid,$mainUid);
            container()->get(ChRoomCodeService::class)->saveRelationStatus($memberIds,$roomInfo->id,$mainUid,$roomInfo->uid,$roomWxid,2);

            foreach ($memberIds as $memberId){
                //社群行为智能标签
                event(new SmartTagRoomBehaviorTrigger($robotId, $memberId, $roomWxid));
            }
        }

    }

    private function dismissRoom(string $roomId)
    {
        $this->mapper->model::query()
            ->where('room_wxid', $roomId)
            ->delete();

        ChRoomMember::query()
            ->where('room_wxid', $roomId)
            ->delete();
    }

    /**
     * 操作本地通讯录
     * @param ChRobot $robotInfo
     * @return void
     */
    public function manageLocalContacts(ChRobot $robotInfo): void
    {
        $service = make(\App\Common\Robot\Robot::class,[$robotInfo->server_id,$robotInfo->uuid])->gateway();
        $result = $service->getSessionList();
        //通讯录列表
        $robotId = $robotInfo->id;
        if ($result !== false && !empty($result['collect_list'])) {
            $collectList = $result['collect_list'];
            $roomIds = collect($collectList)->filter(function ($item) {
                return $item['session_type'] == 1;
            })->pluck('session_id')->toArray();

            //保存通讯录
            $roomList = ChRoom::query()
                ->whereHas('robots', function ($query) use ($robotId) {
                    $query->where('robot_id', $robotId);
                })
                ->get();

            $savedRoomId = [];
            $canceledRoomId = [];
            foreach ($roomList as $room) {
                if (!in_array($room->room_wxid, $roomIds)) {
                    $canceledRoomId[] = $room->id;
                } else {
                    $savedRoomId[] = $room->id;
                }
            }

            if(!empty($savedRoomId)){
                ChRoomRobot::query()
                    ->where('robot_id',$robotId)
                    ->whereIn('room_id',$savedRoomId)
                    ->update([
                        'is_saved' => 1
                    ]);
            }

            if(!empty($canceledRoomId)){
                ChRoomRobot::query()
                    ->where('robot_id',$robotId)
                    ->whereIn('room_id',$canceledRoomId)
                    ->update([
                        'is_saved' => 0
                    ]);
            }
        }
    }


    /**
     * 消息置顶
     * @param ChRobot $robot
     * @return void
     */
    public function pinMessage(ChRobot $robotInfo)
    {
        $getFlag = 1 << 2;
        //置顶列表
        $service = make(\App\Common\Robot\Robot::class,[$robotInfo->server_id,$robotInfo->robot_key])->gateway();
        $result = $service->getSessionList();

        if($result !== false && !empty($result['top_list'])){
            $topList = $result['top_list'];
        }
    }
}